package com.wider.foundation;


import com.wider.foundation.Named;
import java.io.PrintStream;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

/**
 * Utility class provides Application-wide module-based Debug Loggine service.
 * The main program should call the static function <code>DLogger.getRoot()</code> to get the root interface of the DLogger.
 * Then use the <code>setStream(PrintStream)</code> to init' the Ouput Print Stream for the logger.
 * Call <code>enable()</code> to enable the debug logging.
 * 
 * @author  William Choi <avextk@gmail.com>
 * @see     DLoggerRoot
 * @see     Named
 */
public class DLogger {
    //Public inner enum
    public enum DLevel {
        FATAL, ERROR, WARNG, DEBUG, INFOR, TRACE, VERBO
    }
    
    //Class variables
    protected   static PrintStream    pstream = null;
    protected   static boolean        enabled = false;
    protected   static DLevel        rootbound = DLevel.FATAL;

    protected   static Map<String, DLevel>        classeslevel = new HashMap<String, DLevel>();
    protected   static Map<String, Set<String>> classesmap = new HashMap<String, Set<String>>();
    protected   static Map<String, DLogger>     instancesmap = new HashMap<String, DLogger>();
    
    /**
     * Check if DLogger is enabled for this program
     * @return  True if enabled; False otherwise.
     */
    public static boolean isEnabled(){
        return enabled;
    }

    /**
     * Get the root interface for main program to manage the entire DLogger.
     * @return  DLoggerRoot the root interface of the DLogger.
     */
    public static DLoggerRoot getRoot(){
        return new DLoggerRoot();
    }

    /**
     * For modules to get their DLogger instance. 
     * If the module is NOT instance of the <code>Named</code> interface, the returned DLogger is at Class level of the module.
     * @param module which will output log message through DLogger
     * @return DLogger the module's instance.
     */
    public static DLogger getInstance(Object module){
        String cname = module.getClass().getName();
        String iname = (module instanceof Named) ? ((Named) module).getName() : cname;
        iname = (null == iname) ? cname : iname;
        
        if (!classesmap.containsKey(cname)){
            HashSet<String> instances = new HashSet<String>();
            classesmap.put(cname, instances);
            classeslevel.put(cname, rootbound);
        }
        HashSet<String> instances = (HashSet<String>) classesmap.get(cname);
        if (!instances.contains(iname)){
            DLogger logger = new DLogger(cname + " " + (cname.equals(iname)? "" : iname));
            instancesmap.put(iname, logger);
            instances.add(iname);
        }
        return instancesmap.get(iname);
    }
    
    //Private fields
    private DLevel               bound;
    private String              mod;
    private SimpleDateFormat    df;

    protected DLogger (String mod_name){
        bound   = DLogger.rootbound;
        mod     = mod_name;
        df      = new SimpleDateFormat("dd-MM-yyy HH:mm:ss  ");
    }

    protected void setLevel(DLevel level){
        bound = level;
    }

    private void log(DLevel level, String msg){
        if (level.compareTo(bound) > 0)
            return;
        Date now = new Date();
        pstream.println(df.format(now) + "[" + level + "] [" + mod + "] " + "  " + new Throwable().getStackTrace()[2].getMethodName() + "(): "+ msg);
    }

    public DLevel getLevel(){
        return bound;
    }
    
    public void fatal(String msg){
        if (!DLogger.isEnabled())
            return;
        log(DLevel.FATAL, msg);
    }

    public void fatal(Throwable ex){
        if (!DLogger.isEnabled())
            return;
        log(DLevel.FATAL, "Caught exception: " + ex.toString());
    }

    public void error(String msg){
        if (!DLogger.isEnabled())
            return;
        log(DLevel.ERROR, msg);
    }

    public void error(Throwable ex){
        if (!DLogger.isEnabled())
            return;
        log(DLevel.ERROR, "Caught exception: " + ex.toString());
    }

    public void warn(String msg){
        if (!DLogger.isEnabled())
            return;
        log(DLevel.WARNG, msg);
    }

    public void warn(Throwable ex){
        if (!DLogger.isEnabled())
            return;
        log(DLevel.WARNG, "Caught exception: " + ex.toString());
    }

    public void debug(String msg){
        if (!DLogger.isEnabled())
            return;
        log(DLevel.DEBUG, msg);
    }

    public void debug(Throwable ex){
        if (!DLogger.isEnabled())
            return;
        log(DLevel.DEBUG, "Caught exception: " + ex.toString());
    }

    public void info(String msg){
        if (!DLogger.isEnabled())
            return;
        log(DLevel.INFOR, msg);
    }

    public void verbose(String msg){
        if (!DLogger.isEnabled())
            return;
        log(DLevel.VERBO, msg);
    }

    public void trace(String msg){
        if (!DLogger.isEnabled())
            return;
        log(DLevel.TRACE, msg);
    }
}
